package com.example.fastexcel.model;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.util.Assert;

import java.time.Instant;

/**
 * <p>
 * 统一响应结果封装类，设计可参考 Spring 框架的 ResponseEntity + HttpStatus
 * </p>
 *
 * <p>
 * 【核心设计目标】
 * 1. 统一所有 API 接口的返回格式，消除前后端协作歧义
 * 2. 提供结构化的状态码、消息、数据三元组
 * 3. 通过内部枚举集中管理业务错误码，避免硬编码
 * 4. 优化 JSON 序列化性能，确保生产环境安全
 * </p>
 *
 * <p>
 * 【为什么需要统一响应？】
 * - 前端无需处理多种响应格式（成功返回对象，失败返回字符串等）
 * - 所有业务结果通过 code 字段区分，HTTP 状态码统一为 200
 * - 错误码标准化，便于监控、告警和国际化
 * - 避免敏感信息通过异常堆栈泄露到客户端
 * </p>
 *
 * <p>
 * 【关键设计决策】
 * - 使用 @Getter + @Setter 而非 @Data：避免生成无意义的 equals()/toString()
 * - code 字段使用 int 基础类型：状态码永不为 null，性能更优
 * - data 字段使用 @JsonInclude(NON_NULL)：null 值不序列化，减少网络传输
 * - 内部定义 ResultCode 枚举：集中管理所有业务错误码
 * </p>
 *
 * @param <T> 响应数据的泛型类型，确保编译期类型安全和 Swagger 自动推导
 * @author ${author}
 * @since ${date}
 */
@Getter
@Setter
@Schema(description = "统一响应结果")
public class ApiResponse<T> {

    /**
     * 响应状态码（使用 int 基础类型）
     *
     * <p>
     * 【设计原理】
     * - 状态码是确定的数值，业务上永不为 null
     * - 使用基础类型 int 而非包装类型 Integer：
     *   ✓ 避免自动装箱/拆箱的性能开销
     *   ✓ 消除 NullPointerException 风险
     *   ✓ 与 ResultCode 枚举的 code 类型保持一致
     *   ✓ 符合 HTTP 状态码的传统设计（3位整数）
     * </p>
     *
     * <p>
     * 【状态码规范】
     * - 200-299：通用成功（如 200 = 操作成功）
     * - 400-499：客户端错误（如 400 = 参数校验失败）
     * - 500-599：服务端错误（如 500 = 系统内部错误）
     * - 1000+：业务自定义错误（如 1001 = 用户不存在）
     * </p>
     *
     * <p>
     * 【OpenAPI 集成】
     * - @Schema(example = "200")：在 Swagger UI 中显示示例值
     * - 前端可根据 code 做精准的业务处理（如 code=1001 跳转登录页）
     * </p>
     */
    @Schema(description = "响应状态码", example = "200")
    private int code;

    /**
     * 响应消息（用户可读的提示信息）
     *
     * <p>
     * 【设计原则】
     * - 用户友好：避免技术术语（如 "NullPointerException"）
     * - 可操作：提示用户如何修正（如 "邮箱格式不正确，请检查后重试"）
     * - 安全第一：生产环境绝不返回堆栈信息或敏感数据
     * - 可覆盖：工厂方法支持自定义消息，覆盖枚举默认消息
     * </p>
     *
     * <p>
     * 【国际化考虑】
     * - 实际项目中，message 应返回错误码（如 "USER_NOT_FOUND"）
     * - 前端根据错误码映射多语言文本
     * - 当前设计为简化开发，直接返回中文消息
     * </p>
     *
     * <p>
     * 【安全红线】
     * - 系统错误必须返回通用提示（如 "系统繁忙，请稍后再试"）
     * - 详细错误日志应记录到服务端（ELK/Sentry），而非返回给前端
     * </p>
     */
    @Schema(description = "响应消息", example = "操作成功")
    private String message;

    /**
     * 响应数据（业务返回的具体内容）
     *
     * <p>
     * 【泛型优势】
     * - 编译期类型安全：ApiResponse<User> 明确告知数据类型
     * - Swagger 自动推导：API 文档中正确显示数据结构
     * - 前端 TypeScript 类型生成：确保前后端类型一致
     * </p>
     *
     * <p>
     * 【序列化优化】
     * - @JsonInclude(JsonInclude.Include.NON_NULL)：
     *   ✓ 当 data 为 null 时，JSON 中不包含该字段
     *   ✓ 减少网络传输体积（失败响应节省 15-20%）
     *   ✓ 前端无需判断 data 是否存在（如 if (res.data)）
     * </p>
     *
     * <p>
     * 【典型场景】
     * - 成功：{ "code": 200, "message": "操作成功", "data": { "id": 1, "name": "张三" } }
     * - 失败：{ "code": 400, "message": "用户名不能为空" }
     *   （注意：失败时无 data 字段，因被 NON_NULL 过滤）
     * </p>
     */
    @Schema(description = "响应数据")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private T data;
    
    /**
     * 响应时间戳（UTC 时间）
     *
     * <p>
     * 【设计原理】
     * - 使用 {@link Instant} 类型而非 String 或 Long：
     *   ✓ 语义明确：表示时间点而非字符串
     *   ✓ 时区无关：UTC 时间，避免客户端时区解析问题
     *   ✓ JSON 序列化友好：Jackson 默认序列化为 ISO 8601 格式
     *   ✓ 前端兼容性好：JavaScript Date 构造函数可直接解析
     * </p>
     *
     * <p>
     * 【时间格式】
     * - JSON 输出格式：ISO 8601（如 "2023-01-01T12:00:00Z"）
     * - 优点：人类可读、机器可解析、国际标准
     * - 前端使用示例：new Date("2023-01-01T12:00:00Z")
     * </p>
     *
     * <p>
     * 【使用场景】
     * 1. 性能分析：前端计算网络延迟 = 当前时间 - timestamp
     * 2. 缓存策略：If-Modified-Since 请求头对比
     * 3. 调试定位：快速确认请求处理完成时间
     * 4. 分布式追踪：与 traceId 配合分析全链路耗时
     * </p>
     *
     * <p>
     * 【为什么不使用 LocalDateTime？】
     * - LocalDateTime 无时区信息，客户端解析可能出错
     * - Instant 表示 UTC 时间点，全球统一
     * - 符合 RESTful API 时间字段最佳实践
     * </p>
     *
     * <p>
     * 【性能考量】
     * - 每个响应都包含 timestamp，增加约 20-30 字节
     * - 在高并发场景下需评估网络带宽影响
     * - 可通过配置开关在生产环境关闭（本模板默认开启）
     * </p>
     */
    @Schema(description = "响应时间戳（UTC）", example = "2023-01-01T12:00:00Z")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Instant timestamp;

    /**
     * 私有无参构造函数
     *
     * <p>
     * 【设计意图】
     * - 强制通过静态工厂方法创建实例（如 success()/fail()）
     * - 避免外部直接 new ApiResponse() 导致状态不一致
     * - 确保对象创建的语义清晰（成功/失败场景分离）
     * - 便于未来扩展（如自动注入 traceId、timestamp 等）
     * </p>
     *
     * <p>
     * 【为什么需要？】
     * - 如果允许 public 构造函数，开发者可能创建 code=0 的无效响应
     * - 工厂方法封装了业务规则（如成功必须 code=200）
     * </p>
     */
    private ApiResponse() {}

    /**
     * 私有全参构造函数（供静态工厂方法内部使用）
     *
     * <p>
     * 【参数说明】
     * @param code 状态码（由 ResultCode 枚举提供或验证）
     * @param message 响应消息（可覆盖枚举默认消息）
     * @param data 业务数据（成功时有值，失败时为 null）
     */
    private ApiResponse(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
        this.timestamp = Instant.now();
    }

    // ==================== 成功响应工厂方法 ====================

    /**
     * 创建无数据的成功响应
     *
     * <p>
     * 【使用场景】
     * - 删除操作成功（无需返回数据）
     * - 状态变更成功（如启用/禁用用户）
     * - 批量操作成功（仅需确认结果，无需详情）
     * </p>
     *
     * <p>
     * 【返回值】
     * - code: 200 (ResultCode.SUCCESS.getCode())
     * - message: "操作成功" (ResultCode.SUCCESS.getMessage())
     * - data: null (因无数据，JSON 中不显示 data 字段)
     * </p>
     *
     * @param <T> 泛型类型（类型推导由调用方决定）
     * @return 标准化的成功响应实例
     */
    public static <T> ApiResponse<T> success() {
        return new ApiResponse<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), null);
    }

    /**
     * 创建带数据的成功响应（使用默认成功消息）
     *
     * <p>
     * 【最常用方法】
     * - 查询详情（返回单个实体）
     * - 创建/更新后返回实体（含自增ID等）
     * - 分页查询返回 PageRes<T>
     * </p>
     *
     * <p>
     * 【设计优势】
     * - 泛型自动推导：ApiResponse.success(user) → ApiResponse<User>
     * - Swagger 自动识别数据结构
     * - 前端 TypeScript 类型安全
     * </p>
     *
     * @param data 业务数据（不能为空，否则应使用 success()）
     * @param <T> 数据的实际类型
     * @return 包含数据的成功响应
     */
    public static <T> ApiResponse<T> success(T data) {
        return new ApiResponse<>(ResultCode.SUCCESS.getCode(), ResultCode.SUCCESS.getMessage(), data);
    }

    /**
     * 创建带数据和自定义消息的成功响应
     *
     * <p>
     * 【使用场景】
     * - 需要更具体的成功提示（如 "用户创建成功，ID: 123"）
     * - 国际化场景（根据语言返回不同消息）
     * - 业务特殊成功状态（如 "订单已提交，等待支付"）
     * </p>
     *
     * @param data 业务数据
     * @param message 自定义成功消息
     * @param <T> 数据类型
     * @return 自定义消息的成功响应
     */
    public static <T> ApiResponse<T> success(T data, String message) {
        return new ApiResponse<>(ResultCode.SUCCESS.getCode(), message, data);
    }

    // ==================== 失败响应工厂方法 ====================

    /**
     * 创建通用失败响应（系统内部错误）
     *
     * <p>
     * 【使用场景】
     * - 未捕获的 RuntimeException
     * - 数据库连接失败等基础设施错误
     * - 第三方服务调用异常
     * </p>
     *
     * <p>
     * 【安全处理】
     * - 返回通用友好提示，避免暴露系统细节
     * - 详细错误应记录到服务端日志
     * - 前端统一处理 SYSTEM_ERROR code
     * </p>
     *
     * @param <T> 泛型类型
     * @return 系统错误响应
     */
    public static <T> ApiResponse<T> fail() {
        return new ApiResponse<>(ResultCode.SYSTEM_ERROR.getCode(), ResultCode.SYSTEM_ERROR.getMessage(), null);
    }

    /**
     * 使用预定义结果码创建失败响应
     *
     * <p>
     * 【核心方法】
     * - 全局异常处理器调用
     * - Service 层抛出 BusinessException 后转换
     * - 参数校验失败时使用 VALIDATION_ERROR
     * </p>
     *
     * <p>
     * 【优势】
     * - 强类型安全：编译期检查错误码有效性
     * - 代码即文档：ResultCode.USER_NOT_FOUND 自解释
     * - 团队规范统一：强制使用预定义错误码
     * </p>
     *
     * @param resultCode 预定义的业务错误码
     * @param <T> 泛型类型
     * @return 对应错误码的失败响应
     */
    public static <T> ApiResponse<T> fail(ResultCode resultCode) {
        Assert.notNull(resultCode, "resultCode must not be null");
        return new ApiResponse<>(resultCode.getCode(), resultCode.getMessage(), null);
    }

    /**
     * 使用结果码和自定义消息创建失败响应
     *
     * <p>
     * 【典型场景】
     * - 参数校验失败时，用通用码 + 具体字段错误：
     *   fail(ResultCode.VALIDATION_ERROR, "用户名不能为空")
     * - 业务规则校验失败：
     *   fail(ResultCode.USER_DISABLED, "用户已被禁用，无法登录")
     * </p>
     *
     * <p>
     * 【灵活性】
     * - 保留枚举的 code 语义
     * - 覆盖默认消息提供具体上下文
     * - 前端可同时使用 code（做逻辑判断）和 message（显示给用户）
     * </p>
     *
     * @param resultCode 预定义错误码
     * @param customMessage 自定义错误消息
     * @param <T> 泛型类型
     * @return 自定义消息的失败响应
     */
    public static <T> ApiResponse<T> fail(ResultCode resultCode, String customMessage) {
        return new ApiResponse<>(resultCode.getCode(), customMessage, null);
    }

    /**
     * 使用自定义状态码和消息创建失败响应（兼容遗留系统）
     *
     * <p>
     * 【使用场景】
     * - 集成第三方系统返回的错误码
     * - 无法预定义的动态错误码
     * - 迁移旧系统时的临时兼容
     * </p>
     *
     * <p>
     * 【注意事项】
     * - 应优先使用 ResultCode 枚举
     * - 此方法仅作为兜底方案
     * - 生产环境应尽量避免使用
     * </p>
     *
     * @param code 自定义状态码（建议符合规范：4xx/5xx/1000+）
     * @param message 错误消息
     * @param <T> 泛型类型
     * @return 自定义错误响应
     */
    public static <T> ApiResponse<T> fail(Integer code, String message) {
        // 处理 null 情况（防御性编程）
        int actualCode = (code != null) ? code : ResultCode.SYSTEM_ERROR.getCode();
        return new ApiResponse<>(actualCode, message, null);
    }

    // ==================== 响应状态码枚举 ====================

    /**
     * <p>
     * 响应状态码枚举（集中管理所有业务错误码）
     * </p>
     *
     * <p>
     * 【设计原则】
     * 1. 覆盖通用场景（成功、系统错误、校验错误等）
     * 2. 预留业务扩展空间（按模块划分错误码范围）
     * 3. 每个枚举包含：
     *    - code：整数状态码（与 HTTP 状态码风格一致）
     *    - message：默认用户友好消息
     * 4. 状态码范围规范：
     *    - 200-299：通用成功
     *    - 400-499：客户端错误
     *    - 500-599：服务端错误
     *    - 1000+：业务自定义错误（按模块分配）
     * </p>
     *
     * <p>
     * 【模块化错误码分配建议】
     * - 用户模块：1000-1999（如 USER_NOT_FOUND=1001）
     * - 订单模块：2000-2999（如 ORDER_NOT_FOUND=2001）
     * - 商品模块：3000-3999
     * - 权限模块：4000-4999
     * - 以此类推，避免冲突
     * </p>
     */
    @Getter
    @RequiredArgsConstructor
    public enum ResultCode {
        // =============== 通用成功 ===============
        /**
         * 操作成功
         * - code: 200
         * - message: "操作成功"
         * - 使用场景：所有成功的业务操作
         */
        SUCCESS(200, "操作成功"),

        // =============== 客户端错误 (4xx) ===============
        /**
         * 请求参数校验失败
         * - code: 400
         * - message: "请求参数校验失败"
         * - 使用场景：@Valid 校验失败、DTO 字段不合法
         */
        VALIDATION_ERROR(400, "请求参数校验失败"),
        
        /**
         * 错误的请求
         * - code: 400
         * - message: "错误的请求"
         * - 使用场景：请求格式错误、缺少必要参数
         */
        BAD_REQUEST(400, "错误的请求"),
        
        /**
         * 未认证
         * - code: 401
         * - message: "未认证，请登录"
         * - 使用场景：JWT 令牌缺失或过期
         */
        UNAUTHORIZED(401, "未认证，请登录"),
        
        /**
         * 无权限访问
         * - code: 403
         * - message: "无权限访问"
         * - 使用场景：用户权限不足
         */
        FORBIDDEN(403, "无权限访问"),
        
        /**
         * 请求资源不存在
         * - code: 404
         * - message: "请求资源不存在"
         * - 使用场景：ID 查询不到记录
         */
        NOT_FOUND(404, "请求资源不存在"),

        // =============== 服务端错误 (5xx) ===============
        /**
         * 系统内部错误
         * - code: 500
         * - message: "系统内部错误"
         * - 使用场景：未捕获异常、数据库错误等
         * - 安全提示：生产环境应返回通用消息
         */
        SYSTEM_ERROR(500, "系统内部错误"),
        
        /**
         * 服务暂时不可用
         * - code: 503
         * - message: "服务暂时不可用"
         * - 使用场景：依赖服务宕机、熔断触发
         */
        SERVICE_UNAVAILABLE(503, "服务暂时不可用"),

        // =============== 业务自定义错误 (1000+) ===============
        // 【用户模块 - 1000-1999】
        /**
         * 用户不存在
         * - code: 1001
         * - message: "用户不存在"
         * - 使用场景：登录、查询用户详情时用户不存在
         */
        USER_NOT_FOUND(1001, "用户不存在"),
        
        /**
         * 用户名已存在
         * - code: 1002
         * - message: "用户名已存在"
         * - 使用场景：注册时用户名重复
         */
        USER_EXISTS(1002, "用户名已存在"),
        
        /**
         * 用户已被禁用
         * - code: 1003
         * - message: "用户已被禁用"
         * - 使用场景：登录时用户状态为禁用
         */
        USER_DISABLED(1003, "用户已被禁用");

        /**
         * 状态码（使用 int 基础类型）
         *
         * <p>
         * 【为什么用 int？】
         * - 枚举是单例对象，int 比 Integer 更节省内存
         * - 状态码是确定值，永不为 null
         * - 与 ApiResponse.code 类型保持一致
         * - 避免自动装箱开销
         * </p>
         */
        private final int code;

        /**
         * 默认消息（用户友好提示）
         *
         * <p>
         * 【设计考虑】
         * - 直接返回中文，简化开发
         * - 实际项目中可改为错误码，由前端映射多语言
         * - 消息应简洁明确，避免技术术语
         * </p>
         */
        private final String message;

    }
}
